﻿/**
 * Vector2
 * 
 * @author		Allen Chou
 * @version		1.0.4 (last update: Nov 16 2008)
 * @link        http://cjcat.blogspot.com
 * @link		http://cjcat2266.deviantart.com
 */

package idv.cjcat.geom {
	import flash.display.*;
	import flash.events.*;
	import flash.geom.*;
	
	import idv.cjcat.*;
	
	/**
	 * A <code>Vector2</code> object represents a vector in 2D space.
	 */
	public class Vector2 {
		
		public var info:EventDispatcher = new EventDispatcher();
		
		/** @private */
		protected var _x:Number;
		/** @private */
		protected var _y:Number;
		
		/**
		 * @param	object
		 * @return  A <code>Vector2</code> object representing the coordinate of <code>object</code>.
		 */
		public static function posVec(object:DisplayObject):Vector2 {
			return new Vector2(object.x, object.y);
		}
		
		/**
		 * Sets the absolute position of a <code>DisplayObject</code>.
		 * @param	object   The <code>DisplayObject</code>.
		 * @param	vector   The <code>Vector2</code> object representing the position to be set.
		 */
		public static function setPos(object:DisplayObject, vector:Vector2):void {
			object.x = vector.x;
			object.y = vector.y;
		}
		
		/**
		 * Sets the relative position of a <code>DisplayObject</code>.
		 * @param	object   The <code>DisplayObject</code>.
		 * @param	vector   The <code>Vector2</code> object representing the position to be set.
		 */
		public static function stepPos(object:DisplayObject, vector:Vector2):void {
			object.x += vector.x;
			object.y += vector.y;
		}
		
		/**
		 * Sums up <code>Vector2</code> objects.
		 * @param	...vectors  The vectors.
		 * @return  The sum.
		 */
		public static function sum(...vectors):Vector2 {
			var tempX:Number = 0;
			var tempY:Number = 0;
			
			for (var i:int = 0; i < vectors.length; i++) {
				tempX += Vector2(vectors[i]).x;
				tempY += Vector2(vectors[i]).y;
			}
			
			return new Vector2(tempX, tempY);
		}
		
		/**
		 * Returns the difference of a vector from another.
		 * @param	from  The source vector.
		 * @param	to    The target vector.
		 * @return The difference.
		 */
		public static function relative(from:Vector2, to:Vector2):Vector2 {
			return new Vector2(to.x - from.x, to.y - from.y);
		}
		
		public function Vector2(x:Number = 0, y:Number = 0) {
			_x = x;
			_y = y;
		}
		
		/**
		 * Copies a vector.
		 * @return The copy.
		 */
		public function clone():Vector2 {
			return new Vector2(x, y);
		}
		
		/**
		 * Sums two vectors.
		 * @param	vector  Another vector.
		 * @return The sum.
		 */
		public function add(vector:Vector2):Vector2 {
			return new Vector2(x + vector.x, y + vector.y);
		}
		
		/**
		 * Calculates the dot product of two vectors.
		 * @param	vector  Another vector.
		 * @return The dot product.
		 */
		public function dot(vector:Vector2):Number {
			return (x * vector.x) + (y * vector.y);
		}
		
		/**
		 * Calculate the projection of one vector onto another.
		 * @param	target  The target vector.
		 * @return The projected vector obtained.
		 */
		public function project(target:Vector2):Vector2 {
			target = target.unitVec();
			target.length = this.dot(target);
			return target;
		}
		
		/**
		 * Returns the result of a vector after rotated by a specified angle.
		 * @param	angle      The angle of rotation.
		 * @param	radian     Whether the unit of angle is radian; otherwise, it's in degree.
		 * @param	clockwise  Whether the rotation is clockwise; otherwise, it's counterclockwise.
		 * @return The rotated vector.
		 */
		public function rotate(angle:Number, useAngle:Boolean = false, clockwise:Boolean = false):Vector2 {
			var factor:Number = (clockwise)?(1):(-1);
			if (useAngle) angle = angle * Math.PI / 180;
			
			var temp:Vector2 = new Vector2(x * Math.cos(angle) + y * factor * Math.sin(angle), -x * factor * Math.sin(angle) + y * Math.cos(angle));
			temp.length = CJMath.setPrecision(temp.length, 10);
			
			return temp;
		}
		
		/**
		 * Returns the unit vector of a vector.
		 * @return The unit vector.
		 */
		public function unitVec():Vector2 {
			if (length == 0) return new Vector2();
			return new Vector2(CJMath.setPrecision(x / length, 10), CJMath.setPrecision(y / length, 10));
		}
		
		/**
		 * The length of a vecotr, which is sqrt(x^2 + y^2).
		 */
		public function get length():Number {
			return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
		}
		
		public function set length(value:Number):void {
			var leng:Number = length;
			
			_x = _x * value / leng;
			_y = _y * value / leng;
			info.dispatchEvent(new Event(Event.CHANGE));
		}
		
		/**
		 * The x value of the vector.
		 */
		public function get x():Number { return _x; }
		public function set x(value:Number):void {
			_x = value;
			info.dispatchEvent(new Event(Event.CHANGE));
		}
		
		/**
		 * The y value of the vector.
		 */
		public function get y():Number { return _y; }
		public function set y(value:Number):void {
			_y = value;
			info.dispatchEvent(new Event(Event.CHANGE));
		}
		
		/**
		 * Returns a 2D polar vector equivalent of a vector.
		 * @return The polar vector.
		 */
		public function toVector2P():Vector2P {
			return new Vector2P(length, Math.atan2(y, x));
		}
		
		/**
		 * Returns a <code>flash.geom.Point</code> equivalent of a vector.
		 * @return The <code>Point</code> object.
		 */
		public function toPoint():Point {
			return new Point(x, y);
		}
		
		/**
		 * Returns a string representation of a vector.
		 * @return The string.
		 */
		public function toString():String {
			return "(x = " + x + ", y = " + y + ")";
		}
		
	}
	
}